﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace BackupRestore.Logger
{
    /// <summary>
    /// Used for creating logs or adding lines to the logs 
    /// </summary>
    class Logger
    {
        /// <summary>
        /// The debug mode of the current program.  Defauls to NONE
        /// </summary>
        private static RunMode.DebugMode CurrentDebugMode = RunMode.DebugMode.NONE;

        /// <summary>
        /// Getter/setter for the debug mode.  This value is expected to be set by the controller.
        /// </summary>
        public static RunMode.DebugMode LoggerDebugMode
        {
            get
            {
                return CurrentDebugMode;
            }
            set
            {
                CurrentDebugMode = value;
            }
        }

        // File handle for the log file
        private static StreamWriter LogOutputFile;

        // The date today, formatted in DateFormatString
        private static String DateToday;

        // The maximum size a log file can be in bytes.  Currently set to 104857600 bytes (i.e. 100 MB).
        // Upon reaching the limit, a new file is created.
        private static ulong MaxLogFileSize = 104857600;

        // The number of files that have been created today.  This value gets incremented when
        // a)  We try to create a log file, and that file already exists.
        // b)  the log file reaches 100 MB
        // This value gets set to zero 
        private static uint FilesCreatedToday = 0;

        // String format to specify current date
        private static string DateFormatString = "yyyy-MM-dd";

        // String format to specify current time
        private static string TimeFormatString = "yyyy-MM-dd HH:mm:ss.fff";

        // FileInfo object for the log file
        private static FileInfo LogOutputFileInfo;


        /// <summary>
        /// Calculates the name of the current log file being used
        /// </summary>
        /// <returns>returns that name</returns>
        private static String GetCurrentLogFileName()
        {
            return "BackupRestore-" + DateToday + "-" + FilesCreatedToday + ".txt";
        }

        /// <summary>
        /// Initializes log output file.  
        /// </summary>
        private static void SetOutputFile()
        {
            // If we currently do not have a log file, find the next available log file name and use it.
            if (LogOutputFile == null)
            {
                DateToday = DateTime.Now.ToString(DateFormatString);

                LogOutputFile = new StreamWriter(FindLogFileName());
                LogOutputFileInfo = new FileInfo (GetCurrentLogFileName());

                return;
            }

            // We reach this point only if a log file is being used


            string dateNow = DateTime.Now.ToString(DateFormatString);


            // If the date has changed, create a new log file
            if (!dateNow.Equals(DateToday))
            {
                DateToday = DateTime.Now.ToString(DateFormatString);
                FilesCreatedToday = 0;

                string NewLogFileName = FindLogFileName();
                ForceLog("Moving to new log file: " + NewLogFileName);

                LogOutputFile.Close();
                

                LogOutputFile = new StreamWriter(NewLogFileName);
                LogOutputFileInfo = new FileInfo(NewLogFileName);

                return;
            }

            LogOutputFileInfo.Refresh();

            // If the log file has reached 100 MB or more, create a new file
            if ((ulong)(LogOutputFileInfo.Length) >= MaxLogFileSize)
            {
                string NewLogFileName = FindLogFileName();
                ForceLog("Moving to new log file: " + NewLogFileName);

                LogOutputFile.Close();


                LogOutputFile = new StreamWriter(NewLogFileName);
                LogOutputFileInfo = new FileInfo(NewLogFileName);
            }
        }

        /// <summary>
        /// Gets the next available log file name
        /// </summary>
        /// <returns>Next available log file name</returns>
        private static String FindLogFileName()
        {
            while (File.Exists(GetCurrentLogFileName()))
            {
                FilesCreatedToday++;
            }

            return GetCurrentLogFileName();
        }

        /// <summary>
        /// Logs generic message, with the mode passed
        /// </summary>
        /// <param name="modeForThisMessage">The debug mode for the message</param>
        /// <param name="message">the message as a string</param>
 
        public static void Log(RunMode.DebugMode modeForThisMessage, string message)
        {
            //
            if (LoggerDebugMode != RunMode.DebugMode.NONE && (int) LoggerDebugMode >= (int) modeForThisMessage)
            {
                SetOutputFile(); // Call this function to setup the log output file.  Logging won't work without calling this function!

                LogOutputFile.WriteLine(DateTime.Now.ToString(TimeFormatString) + " " + message);
            }
        }

        /// <summary>
        /// Forcefully logs some thing, without checking LoggerDebugMode.  This assumes that SetOutputFile function has already been called.
        /// </summary>
        /// <param name="message"></param>
        private static void ForceLog(string message)
        {
            LogOutputFile.WriteLine(DateTime.Now.ToString(TimeFormatString) + " " + message);
        }

        /// <summary>
        /// Logs method entry
        /// </summary>
        /// <param name="className">class name</param>
        /// <param name="methodName">method name</param>
        public static void LogMethodEntry(string className, string methodName)
        {
            Log(RunMode.DebugMode.ENTRY, String.Format("[{0}.{1}] [ENTRY]", className, methodName));
        }

        /// <summary>
        /// Logs method exit
        /// </summary>
        /// <param name="className">class name</param>
        /// <param name="methodName">method name</param>
        public static void LogMethodExit(string className, string methodName)
        {
            Log(RunMode.DebugMode.ENTRY, String.Format("[{0}.{1}] [EXIT]", className, methodName));
        }

        public static void LogDebug(string className, string methodName, string message)
        {
            Log(RunMode.DebugMode.DEBUG, String.Format("[{0}.{1}] [DEBUG] {2}", className, methodName, message));
        }


        /// <summary>
        /// Logs severe issues (that require program to exit)
        /// </summary>
        /// <param name="className">the class name</param>
        /// <param name="methodName">the method name</param>
        /// <param name="message">any message as a string</param>
        /// <param name="stackTrace">stack trace as a string</param>
        public static void LogSevere(string className, string methodName, string message, string stackTrace)
        {
            Log(RunMode.DebugMode.SEVERE, String.Format("[{0}.{1}] [FATAL] {2}\n{3}", className, methodName, message, stackTrace));
        }

        /// <summary>
        /// Logs any warning messages (i.e. errors where you can continue from by using some default value)
        /// </summary>
        /// <param name="className">class name</param>
        /// <param name="methodName">method name</param>
        /// <param name="message">any message</param>
        public static void LogWarning(string className, string methodName, string message)
        {
            Log(RunMode.DebugMode.WARNING, String.Format("[{0}.{1}] [WARNING] {2}", className, methodName, message));
        }

        /// <summary>
        /// Logs any informational message
        /// </summary>
        /// <param name="className">class name</param>
        /// <param name="methodName">method name</param>
        /// <param name="message">any message</param>
        public static void LogInfo(string className, string methodName, string message)
        {
            Log(RunMode.DebugMode.INFO, String.Format("[{0}.{1}] [INFO]  {2}", className, methodName, message));
        }

        /// <summary>
        /// Logs any messages where you can continue running from but cannot recover previous state
        /// </summary>
        /// <param name="className">class name</param>
        /// <param name="methodName">method name</param>
        /// <param name="message">any message</param>
        public static void LogError(string className, string methodName, string message)
        {
            Log(RunMode.DebugMode.ERROR, String.Format("[{0}.{1}] [ERROR] {2}", className, methodName, message));
        }

        /// <summary>
        /// Call this method before Exiting the program.  It closes the log file handle.
        /// </summary>
        public static void Exiting()
        {
            if (LogOutputFile != null)
            {
                LogOutputFile.Flush();
                LogOutputFile.Close();
            }
        }
    }
}
